package pro.softzhang.algo.lc100;

import pro.softzhang.algo.common.ListNode;
import pro.softzhang.algo.common.Utils;
import pro.softzhang.algo.flag.difficulty.DRed;
import pro.softzhang.algo.flag.knowledge.KDoublePointer;
import pro.softzhang.algo.flag.x.XOptimal;

import java.util.HashSet;
import java.util.Set;

/**
 * 142. 环形链表 II
 * https://leetcode.cn/problems/linked-list-cycle-ii
 */
@DRed
public class LC142_LinkedListCycleII {
    public static void main(String[] args) {
        ListNode head = new ListNode(3, new ListNode(2, new ListNode(0, new ListNode(-4, null))));
        head.next.next.next.next = head.next;
        // Utils.printLinkedList(head);
        // ListNode node = new Solution().detectCycle(head);
        // ListNode node = new Solution1().detectCycle(head);
        ListNode node = new Solution2().detectCycle(head);
        System.out.println(node.val);
    }

    /**
     *
     */
    static
    class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode loopStartNode = getLoopStartNode(head);
            if(loopStartNode == null) {
                return null;
            }

            while (head != loopStartNode) {
                head = head.next;
                loopStartNode = loopStartNode.next;
            }
            return head;
        }

        private ListNode getLoopStartNode(ListNode head) {
            if (head == null || head.next == null) {
                return null;
            }

            ListNode slow = head.next, fast = slow.next;
            while (slow != null && fast != null) {
                if (slow == fast) {
                    return slow;
                }
                slow = slow.next;
                fast = fast.next;
                if (fast != null) {
                    fast = fast.next;
                }
            }
            return null;
        }
    }

    /**
     *
     */
    @KDoublePointer
    @XOptimal
    static
    class Solution1 {
        public ListNode detectCycle(ListNode head) {
            ListNode slow = head, fast = head;
            while (fast != null && fast.next != null) {
                slow = slow.next;
                fast = fast.next.next;
                if (fast == slow) {
                    while (slow != head) {
                        slow = slow.next;
                        head = head.next;
                    }
                    return slow;
                }
            }
            return null;
        }
    }

    static
    class Solution2 {
        public ListNode detectCycle(ListNode head) {
            Set<ListNode> set = new HashSet<>();
            ListNode current = head;
            while (current != null) {
                if (set.contains(current)) {
                    return current;
                }
                set.add(current);
                current = current.next;
            }
            return null;
        }
    }
}